package com.yixin.token.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.pfpj.sm.SM2Utils;
import com.pfpj.sm.SM4Utils;
import com.pfpj.sm.Signature;
import com.yixin.token.config.Config;
import com.yixin.token.config.Env;
import com.yixin.token.config.Function;
import com.yixin.token.config.Merchant;
import com.yixin.token.exceptions.ServerException;
import com.yixin.token.pojo.*;
import com.yixin.token.pojo.token.H5UserInfoRequest;
import com.yixin.token.utils.HttpClientUtils;
import com.yixin.token.utils.SMTool;
import com.yixin.token.utils.SerialNoUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@Slf4j
public class HelloController implements ApplicationRunner {
    Map<String, Env> envMap = new HashMap<>();
    Map<String, Function> functionMap = new HashMap<>();
    Map<String, Merchant> merchantMap = new HashMap<>();
    @Autowired
    private Config config;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        List<Env> envs = config.getEnvs();
        for (Env env : envs) {
            envMap.put(env.getId(), env);
        }
        List<Function> functions = config.getFunctions();
        for (Function function : functions) {
            functionMap.put(function.getId(), function);
        }
        List<Merchant> merchants = config.getMerchants();
        for (Merchant merchant : merchants) {
            merchantMap.put(merchant.getId(), merchant);
        }
    }

    @GetMapping("/config")
    public Object getConfig() {
        return config;
    }

    @GetMapping("/token")
    public Resp token(@RequestParam String envId,
                      @RequestParam String functionId,
                      @RequestParam String merchantId) throws IOException {
        Env env = envMap.get(envId);
        Function function = functionMap.get(functionId);
        Merchant merchant = merchantMap.get(merchantId);
        if (env != null && function != null && merchant != null) {
            return getTokenWithConfig(env, function, merchant);
        }
        return Resp.error("配置错误");
    }

    private Resp getTokenWithConfig(Env env, Function function, Merchant merchant) throws IOException {
        // 商户号
        String merchantId = merchant.getNumber();
        // 应用id
        String appID = merchant.getAppId();
        // 请求url
        String url = env.getUrl() + "/" + merchantId;
        // 合作方私钥
        String privateKey = merchant.getPrivatekey();
        // 合作方公钥
        String publicKey = merchant.getPublickey();
        // 服开与合作方配对公钥
        String sopPublicKey = merchant.getPlatformkey();
        //接口代码
        String method = function.getCode();
        // 2.请求报文报文头
        OpenApiMessageHead msgHead = new OpenApiMessageHead();
        // 合作放交易流水号
        msgHead.setPartnerTxSriNo(SerialNoUtil.getSerialNo());
        // 接口代码
        msgHead.setMethod(method);
        // 接口版本号
        msgHead.setVersion("1");
        // 合作放编号
        msgHead.setMerchantId(merchantId);
        // 应用id
        msgHead.setAppID(appID);
        // 报文发起时间
        msgHead.setReqTime(SerialNoUtil.getDateTime());
        log.debug("原始请求报文头: {}", JSON.toJSONString(msgHead));
        // 3.请求报文报文体
        H5UserInfoRequest request = new H5UserInfoRequest();
        request.setPartnerTxSriNo(SerialNoUtil.getSerialNo());
        request.setMerchantId(merchantId);
        request.setAppID(appID);
        request.setMethod(method);
        request.setVersion("1");
        request.setTxSriNo(SerialNoUtil.getSerialNo());
        request.setBusiMainId(SerialNoUtil.getSerialNo());
        request.setBisCode(method);
        request.setPersonalCertTypeCode("01");

        request.setCliIpAddr("49.73.201.134");
        request.setPrdNo("A012");
        request.setMac("00-1C-42-72-CD-2C");
        request.setOpenSource("4000");
        request.setTxInstNo("1199984Q");
//        request.setIsPreOpen("1");
//        request.setPersonalCertNo("6378221198705232713");
//        request.setCustName("彭钳蝇");
        request.setShowRecomCode("1");
        request.setUserSignatoryId(SerialNoUtil.getSerialNo());
        if ("h5.account.recharge".equals(method)){
            request.setUserSignatoryId("202310131829379730121800");
            request.setPersonalCertNo("341126197709218366");
            request.setCustName("全渠道");
//            request.setMobileNo("13552535506");
            log.info("----------------{}-------------------",method);
        } else if ("h5.account.openAccount".equals(method)){
            request.setPersonalCertNo(null);
            request.setCustName(null);
            log.info("----------------{}-------------------",method);
        }

        // 4. 请求报文对象
        OpenApiMessage<H5UserInfoRequest> reqMsg = new OpenApiMessage<>();

        reqMsg.setHead(msgHead);
        reqMsg.setBody(request);

        // 5. 加密加签后的请求报文对象
        OpenApiRequest openApiRequest = new OpenApiRequest();
        // 5.1 对称加密请求报文
        String orignReqJsonStr = JSON.toJSONString(reqMsg);
        log.debug("原始请求报文: {}", orignReqJsonStr);
        String sm4Key = SMTool.getSM4Key();
        String encryptRequest = SM4Utils.encrypt(orignReqJsonStr, "CBC", sm4Key, "");
        openApiRequest.setRequest(encryptRequest);
        // 5.2 加密sm4密钥
        SM2Utils sm2Utils = new SM2Utils();
        String encryptKey = sm2Utils.encrypt(sopPublicKey, sm4Key);
        openApiRequest.setEncryptKey(encryptKey);
        openApiRequest.setAccessToken("");
        // 5.3 计算签名
        StringBuilder sb = new StringBuilder();
        sb.append(StringUtils.defaultString(openApiRequest.getRequest(), ""));
        sb.append(StringUtils.defaultString(openApiRequest.getEncryptKey(), ""));
        sb.append(StringUtils.defaultString(openApiRequest.getAccessToken(), ""));
        Signature sign = sm2Utils.sign(merchantId, privateKey, sb.toString(), publicKey);
        String signature = SMTool.toSignStr(sign);
        openApiRequest.setSignature(signature);
        log.info(signature);

        // 5.4 发起请求
        // 响应
        log.debug("请求报文: {}", JSON.toJSONString(openApiRequest));
        url = String.format("%s?partnerTxSriNo=%s", url, msgHead.getPartnerTxSriNo());
        String respJson = null;
        try {
            respJson = HttpClientUtils.post(url, JSON.toJSONString(openApiRequest));
        } catch (ServerException e) {
            log.error("调用失败", e);
            return Resp.error(e.getErrorMsg());
        }
        log.debug("原响应报文:{}", respJson);
        JSONObject jsonObject = JSON.parseObject(respJson);
        if (Boolean.FALSE.equals(jsonObject.getBoolean("success"))) {
            return Resp.error(jsonObject);
        }
        // 5.5 解析响应
        OpenApiResponse openApiResponse = JSON.parseObject(respJson, OpenApiResponse.class);
        // 验签
        sb.setLength(0);
        sb.append(StringUtils.defaultString(openApiResponse.getResponse(), ""));
        sb.append(StringUtils.defaultString(openApiResponse.getEncryptKey(), ""));
        sb.append(StringUtils.defaultString(openApiResponse.getAccessToken(), ""));
        boolean checked = false;
        try {
            checked = sm2Utils.verifySign(merchantId, sopPublicKey, sb.toString(), SMTool.fromString(openApiResponse.getSignature()));
        } catch (Exception e) {
            log.error("验签失败", e);
            return Resp.error(e);
        }
        if (checked) {
            // 解析密钥
            String respSm4Key = sm2Utils.decrypt(privateKey, openApiResponse.getEncryptKey());
            log.info("------------------------------");
            log.debug("响应报文sm4密钥:{}", respSm4Key);
            // 解析报文
            String respMessage = SM4Utils.decrypt(openApiResponse.getResponse(), "CBC", respSm4Key, "");
            log.debug("响应报文:{}", respMessage);
            JSONObject result = JSON.parseObject(respMessage);
            result.put("prefix", env.getPrefix() + "/" + function.getUrl() + "/?accessToken=");

            return Resp.ok(result);
        } else {
            log.error("响应报文验签失败!");
            return Resp.error(openApiResponse);
        }

    }

    @GetMapping("/hello")
    public Object getToken() {

        try {
            // 商户号
            String merchantId = "accountGroup01";
            // 应用id
            String appID = "1019934064194560000001";
            // 请求url
            String url = "http://220.248.252.123:8443/sop-h5/h5/token/apply/accountGroup01";
            // 合作方私钥
            String privateKey = "00EC1668E0D48FA49D3A12779C57DEEF9FA718C5462992B441D5100B1332061A23";
            // 合作方公钥
            String publicKey = "0476F67DC0A2CE6555C0BB32F2E39477013AA3EB30E980E08105D1A808929593895849CD894B8B90756CE12814312098FB3ABBF147CEF29A3814D6D02210911028";
            // 服开与合作方配对公钥
            String sopPublicKey = "041782B14D14222B615D019731129640068B100FEEB46751BB137F3FA906CCFE8F1F7B4348279D9EF337E8ABA3ACCB52F0EDBDC08A197E36229EA12441EA858E52";


            // 2.请求报文报文头
            OpenApiMessageHead msgHead = new OpenApiMessageHead();
            // 合作放交易流水号
            msgHead.setPartnerTxSriNo(SerialNoUtil.getSerialNo());
            // 接口代码
            msgHead.setMethod("h5.account.openAccount");
            // 接口版本号
            msgHead.setVersion("1");
            // 合作放编号
            msgHead.setMerchantId(merchantId);
            // 应用id
            msgHead.setAppID(appID);
            // 报文发起时间
            msgHead.setReqTime(SerialNoUtil.getDateTime());
            log.debug("原始请求报文头: {}", JSON.toJSONString(msgHead));
            // 3.请求报文报文体
            H5UserInfoRequest request = new H5UserInfoRequest();
            request.setPartnerTxSriNo(SerialNoUtil.getSerialNo());
            request.setMerchantId(merchantId);
            request.setAppID(appID);
            request.setMethod("h5.account.openAccount");
            request.setVersion("1");
            request.setTxSriNo(SerialNoUtil.getSerialNo());
            request.setBusiMainId(SerialNoUtil.getSerialNo());
            request.setBisCode("h5.account.openAccount");
//            request.setCustId("");
//            request.setCustName("");
            request.setPersonalCertTypeCode("01");
            request.setUserSignatoryId(SerialNoUtil.getSerialNo());
            request.setCliIpAddr("127.0.0.1");
            request.setPrdNo("A012");
            request.setMac("00-1C-42-72-CD-2C");
            request.setOpenSource("4000");
            request.setTxInstNo("11009021");

            // 4. 请求报文对象
            OpenApiMessage<H5UserInfoRequest> reqMsg = new OpenApiMessage<>();

            reqMsg.setHead(msgHead);
            reqMsg.setBody(request);

            // 5. 加密加签后的请求报文对象
            OpenApiRequest openApiRequest = new OpenApiRequest();
            // 5.1 对称加密请求报文
            String orignReqJsonStr = JSON.toJSONString(reqMsg);
            log.debug("原始请求报文: {}", orignReqJsonStr);
            String sm4Key = SMTool.getSM4Key();
            String encryptRequest = SM4Utils.encrypt(orignReqJsonStr, "CBC", sm4Key, "");
            openApiRequest.setRequest(encryptRequest);
            // 5.2 加密sm4密钥
            SM2Utils sm2Utils = new SM2Utils();
            String encryptKey = sm2Utils.encrypt(sopPublicKey, sm4Key);
            openApiRequest.setEncryptKey(encryptKey);
            openApiRequest.setAccessToken("");
            // 5.3 计算签名
            StringBuilder sb = new StringBuilder();
            sb.append(StringUtils.defaultString(openApiRequest.getRequest(), ""));
            sb.append(StringUtils.defaultString(openApiRequest.getEncryptKey(), ""));
            sb.append(StringUtils.defaultString(openApiRequest.getAccessToken(), ""));
            Signature sign = sm2Utils.sign(merchantId, privateKey, sb.toString(), publicKey);
            String signature = SMTool.toSignStr(sign);
            openApiRequest.setSignature(signature);
            log.info(signature);

            // 5.4 发起请求
            // 响应
            log.debug("请求报文: {}", JSON.toJSONString(openApiRequest));
            url = String.format("%s?partnerTxSriNo=%s", url, msgHead.getPartnerTxSriNo());
            String respJson = HttpClientUtils.post(url, JSON.toJSONString(openApiRequest));
            // 5.5 解析响应
            OpenApiResponse openApiResponse = JSON.parseObject(respJson, OpenApiResponse.class);
            // 验签
            sb.setLength(0);
            sb.append(StringUtils.defaultString(openApiResponse.getResponse(), ""));
            sb.append(StringUtils.defaultString(openApiResponse.getEncryptKey(), ""));
            sb.append(StringUtils.defaultString(openApiResponse.getAccessToken(), ""));
            boolean checked = sm2Utils.verifySign(merchantId, sopPublicKey, sb.toString(), SMTool.fromString(openApiResponse.getSignature()));
            if (checked) {
                // 解析密钥
                String respSm4Key = sm2Utils.decrypt(privateKey, openApiResponse.getEncryptKey());
                log.info("------------------------------");
                log.debug("响应报文sm4密钥:{}", respSm4Key);
                // 解析报文
                String respMessage = SM4Utils.decrypt(openApiResponse.getResponse(), "CBC", respSm4Key, "");
                log.debug("响应报文:{}", respMessage);
                return respMessage;
            } else {
                log.error("响应报文验签失败!");
            }

        } catch (Exception e) {
            log.error("发起交易异常: ", e);
        }

        return "hello";
    }


}
